package org.jasper.plugin;

import org.apache.commons.beanutils.PropertyUtils;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRRewindableDataSource;

public abstract class JRAbstractBeanDatasource implements JRRewindableDataSource {

	public JRAbstractBeanDatasource(boolean isUseFieldDescription) {
		propertyNameProvider = isUseFieldDescription ? FIELD_DESCRIPTION_PROPERTY_NAME_PROVIDER
				: FIELD_NAME_PROPERTY_NAME_PROVIDER;
	}

	/**
	 * Field mapping that produces the current bean.
	 * <p/>
	 * If the field name/description matches this constant (the case is
	 * important), the data source will return the current bean as the field
	 * value.
	 */
	public static final String CURRENT_BEAN_MAPPING = "_THIS";

	/**
		 *
		 */
	protected PropertyNameProvider propertyNameProvider;

	protected static final PropertyNameProvider FIELD_NAME_PROPERTY_NAME_PROVIDER = new PropertyNameProvider() {
		public String getPropertyName(JRField field) {
			return field.getName();
		}
	};

	protected static final PropertyNameProvider FIELD_DESCRIPTION_PROPERTY_NAME_PROVIDER = new PropertyNameProvider() {
		public String getPropertyName(JRField field) {
			if (field.getDescription() == null) {
				return field.getName();
			}
			return field.getDescription();
		}
	};

	/**
		 *
		 */
	interface PropertyNameProvider {
		public String getPropertyName(JRField field);
	}

	protected Object getFieldValue(Object bean, JRField field)
			throws JRException {
		return getBeanProperty(bean, getPropertyName(field));
	}

	protected static Object getBeanProperty(Object bean, String propertyName)
			throws JRException {
		Object value = null;

		if (isCurrentBeanMapping(propertyName)) {
			value = bean;
		} else if (bean != null) {
			try {
				value = PropertyUtils.getProperty(bean, propertyName);
			} catch (java.lang.IllegalAccessException e) {
				throw new JRException(
						"Error retrieving field value from bean : "
								+ propertyName, e);
			} catch (java.lang.reflect.InvocationTargetException e) {
				throw new JRException(
						"Error retrieving field value from bean : "
								+ propertyName, e);
			} catch (java.lang.NoSuchMethodException e) {
				throw new JRException(
						"Error retrieving field value from bean : "
								+ propertyName, e);
			} catch (IllegalArgumentException e) {
				// FIXME replace with NestedNullException when upgrading to
				// BeanUtils 1.7
				if (!e.getMessage().startsWith("Null property value for ")) {
					throw e;
				}
			}
		}

		return value;
	}

	protected static boolean isCurrentBeanMapping(String propertyName) {
		return CURRENT_BEAN_MAPPING.equals(propertyName);
	}

	protected String getPropertyName(JRField field) {
		return propertyNameProvider.getPropertyName(field);
	}

}
